﻿using System.Runtime.Serialization;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Decisions.HL7.Message;
using Decisions.HL7.Parser;
using System.Reflection;

namespace Decisions.HL7.DataTypes
{
    [DataContract]
    public abstract class AbstractDataType : IParseable
    {
        // Default Constructor needed for Reflection
        public AbstractDataType()
        {

        }

        public virtual void Parse(string value, HL7ParserOptions options, string dataName, char? whichSep)
        {
          
            // Now we have some string and some delimiter and we can assign data in.
            string[] segmentDataParts = value.Split(new char[] { whichSep ?? options.ComponentSep }, StringSplitOptions.None);
            PropertyDescriptor[] segmentsParts = ReflectionUtility.GetPropertiesForClass(this.GetType());

            for (int i = 0; i < segmentsParts.Length; i++)
            {
                Debug.WriteLine("Parsing type: " + this.GetType());
                Debug.WriteLine("Parsing: " + segmentsParts[i].DataType + " " + segmentsParts[i].Name);

                // If we have a part more than accessor.... check parser options. maybe ignore.
                if (i >= segmentsParts.Length)
                {
                    // @TODO HSO TODO - store off the extra data
                    break;
                }
                if (i >= segmentDataParts.Length) { 
                    // Not enough data.
                    return;
                }

                if (typeof(IParseable).IsAssignableFrom(segmentsParts[i].DataType))
                {
                    // If this is an IPArseable we can just 
                    // pass it in and let the type parse itself.
                    // Instantiate the type
                    object dataType = Activator.CreateInstance(segmentsParts[i].DataType);
                    ((IParseable)dataType).Parse(segmentDataParts[i], options, string.Format("{0}-{1} {2}", dataName, i+1, segmentsParts[i].Name), options.SubComponentSep);  
                    
                    // Just try to set the value
                    segmentsParts[i].SetValue(this, dataType);
                }
                else
                {
                    // Just try to set the value
                    segmentsParts[i].SetValue(this, segmentDataParts[i]);
                }
            }
        }

        public abstract string GetER7(HL7ParserOptions options); // Options includes version

        protected string GetER7TextForDataPart(AbstractDataType pieceOfData, HL7ParserOptions options)
        {
            if (pieceOfData == null) return null;
            return pieceOfData.GetER7(options);
        }

        public string Output(Message.HL7Version version, string delimiter)
        {
            throw new NotImplementedException();
        }

        public string CleanUpTrailingComponentSeps(string sourceER7, HL7ParserOptions options) {


            sourceER7 = sourceER7.TrimEnd(new[] { '^', '~' });
           
            return sourceER7;
        }
    }
}
